// author: Shelman
// description: 封装的使用posix接口实现的TCP服务端

#include "TcpServer.h"

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#endif

#include "Common/CommLogDef.h"


/*****************************************************
 * 功能：构造函数
 * ***************************************************/
TcpServer::TcpServer()
{
#ifdef _WIN32
    // Windows Socket初始化
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
}

/*****************************************************
 * 功能：析构函数
 * ***************************************************/
TcpServer::~TcpServer()
{
#ifdef _WIN32
    WSACleanup();
#endif
}

/*****************************************************
 * 功能：不主动调用read接口获取数据时，设置接收数据的回调，
 * 输入参数：
 *      pfnCallback：数据回调函数
 * ***************************************************/
void TcpServer::setDataRecvCallback(const DataReceivedCallback &pfnCallback)
{
    std::lock_guard<std::mutex> lock(m_mt4PfnCallback);
    m_pfn4RecvDataCb = pfnCallback;
    return;
}

/*****************************************************
 * 功能：开启服务
 * 输入参数：
 *      stConnInfo：服务端的连接信息
 * 返回值：
 *      bool：开启结果，true -- 成功，false -- 失败
 * ***************************************************/
bool TcpServer::start(const NetworkConnInfo &stConnInfo)
{
    if (m_bRunning) {
        COMMONLOG_ERROR("%s", "Server has been start, please stop it first.");
        return false;
    } else {
        m_stConnInfo = stConnInfo;

        m_iSrvSockFd = socket(AF_INET, SOCK_STREAM, 0);
        if (0 > m_iSrvSockFd) {
            COMMONLOG_ERROR("%s", "Server open socket failed.");
            return false;
        }

        // 设置端口重复使用
        int bReuseAddr = 1;    // 设置端口是否重用 1 = true
        setsockopt(m_iSrvSockFd, SOL_SOCKET, SO_REUSEADDR, (char*)(&bReuseAddr), sizeof(bReuseAddr));

        // 设置receive超时时间
        struct timeval stRecvTimeout = {5, 0};   //{秒，微妙}
        setsockopt(m_iSrvSockFd, SOL_SOCKET, SO_RCVTIMEO, (char*)(&stRecvTimeout), sizeof(stRecvTimeout));

        // 绑定地址和端口
        struct sockaddr_in stSrvSockInfo;
        memset(&stSrvSockInfo, 0, sizeof(stSrvSockInfo));
        stSrvSockInfo.sin_family = AF_INET;
        stSrvSockInfo.sin_port = htons(m_stConnInfo.ui16Port);
        //stSrvSockInfo.sin_addr.s_addr = INADDR_ANY;  // 监听所有网卡
        //stSrvSockInfo.sin_addr.s_addr  = inet_addr(m_stConnInfo.strIP.data());
#ifdef _WIN32
        stSrvSockInfo.sin_addr.s_addr = inet_addr(m_stConnInfo.strIP.c_str());
        if (stSrvSockInfo.sin_addr.s_addr == INADDR_NONE) {
            COMMONLOG_ERROR("Invalid IP address: %s.", m_stConnInfo.strIP.data());
            return false;
        }
#else
        if (inet_aton(m_stConnInfo.strIP.c_str(), &stSrvSockInfo.sin_addr) == 0) {
            COMMONLOG_ERROR("Invalid IP address: %s.", m_stConnInfo.strIP.data());
            return false;
        }
#endif

        if (0 > bind(m_iSrvSockFd, (struct sockaddr*)&stSrvSockInfo, sizeof(stSrvSockInfo))) {
            COMMONLOG_ERROR("TcpServer Bind port (%u) failed.", m_stConnInfo.ui16Port);
            closeSocket(m_iSrvSockFd);
            return false;
        }

        // 开始监听
        if (0 > listen(m_iSrvSockFd, 32)) { // 后备队列长度为10
            COMMONLOG_ERROR("%s", "TcpServer Listen failed.");
            closeSocket(m_iSrvSockFd);
            return false;
        }

        // 置为标识
        m_bRunning = true;

        // 启动接受连接的线程
        m_th4AcceptClient = std::thread(&TcpServer::acceptConnections, this);
        COMMONLOG_INFO("TcpServer is started on port: %u.", m_stConnInfo.ui16Port);
    }

    return true;
}

/*****************************************************
 * 功能：停止服务
 * ***************************************************/
void TcpServer::stop()
{
    if (m_bRunning) {
        m_bRunning = false;
        if (m_th4AcceptClient.joinable()) {
            m_th4AcceptClient.join();
        }

        closeSocket(m_iSrvSockFd);
        m_iSrvSockFd = -1;
    }
    return;
}

/*****************************************************
 * 功能：向客户端发送数据
 * 输入参数：
 *      iClientSockFd：客户端socket通信句柄
 *      strData：待发送的数据
 * 返回值：
 *      bool：发送结果，true -- 发送成功，false -- 发送失败
 * ***************************************************/
bool TcpServer::sendData(int iClientSockFd, const std::string &strData)
{
    //std::lock_guard<std::mutex> lock(m_mt4SendData);
    if (!m_bRunning || iClientSockFd < 0) {
        return false;
    }
#if 0
    int bytesSent = send(iClientSockFd, strData.c_str(), strData.length(), 0);
    return bytesSent == static_cast<int>(strData.length());
#else
    bool bRet = true;
    size_t sTotalSent = 0;
    size_t sDataLen = strData.length();

    while (sTotalSent < sDataLen) {
        int iSent = send(iClientSockFd, strData.c_str() + sTotalSent, sDataLen - sTotalSent, 0);
        if (0 <= iSent) {
            sTotalSent += iSent;
        } else {
#ifdef _WIN32
            int error = WSAGetLastError();
            if (error == WSAEWOULDBLOCK) {
                // 非阻塞模式下缓冲区满，稍等继续发送
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                continue;
            } else {
                COMMONLOG_ERROR("Failed to send data: %d.", error);
                bRet = false;
                break;
            }
#else
            if (errno == EWOULDBLOCK || errno == EAGAIN) {
                // 非阻塞模式下缓冲区满，稍等继续发送
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                continue;
            } else {
                COMMONLOG_ERROR("Failed to send data: %s.", strerror(errno));
                bRet = false;
                break;
            }
#endif
        }
    }
    return bRet;
#endif
}

/*****************************************************
 * 功能：向客户端读取数据
 * 输出参数：
 *      iClientSockFd：客户端socket通信句柄
 *      strData：待读取的数据的缓存区
 * 返回值：
 *      bool：读取结果，true -- 读取成功，false -- 读取失败
 * ***************************************************/
bool TcpServer::recvData(int iClientSockFd, std::string &strData)
{
    //std::lock_guard<std::mutex> lock(m_mt4RecvData);
    if (!m_bRunning || iClientSockFd < 0) {
        return false;
    }

    char szBuffer[RECVDATA_DEFAULT_SIZE] = {0};
    int iRecvLen = recv(iClientSockFd, szBuffer, sizeof (szBuffer), 0);
    if (0 < iRecvLen) {
        strData = std::string(szBuffer, iRecvLen);
        return true;
    }
    
    return false;
}

/*****************************************************
 * 功能：获取客户端地址信息
 * 输出参数：
 *      iClientSockFd：客户端socket通信句柄
 * 返回值：
 *      std::string：客户端地址信息，格式ip:port
 * ***************************************************/
std::string TcpServer::getClientAddrInfo(int iClientSockFd)
{
    std::lock_guard<std::mutex> lock(m_mt4ClientFds);
    if (m_mp4ClientFds.find(iClientSockFd) != m_mp4ClientFds.end()) {
        return m_mp4ClientFds[iClientSockFd];
    }
    return "";
}

/*****************************************************
 * 功能：处理客户端的连接
 * ***************************************************/
void TcpServer::acceptConnections()
{
    COMMONLOG_INFO("%s", "Enter tcp server accept connection thread.");
    while (m_bRunning) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(m_iSrvSockFd, &readfds);

        // 设置超时时间
        struct timeval timeout;
        timeout.tv_sec = 3; // 1秒超时
        timeout.tv_usec = 0;

        // 使用 select 检测是否有连接到达
        int result = select(m_iSrvSockFd + 1, &readfds, nullptr, nullptr, &timeout);
        if (result < 0) {
            if (m_bRunning) {
                COMMONLOG_ERROR("%s", "Tcp server select error!");
            }

            break;
        } else if (0 == result) {
            // 超时，没有连接到达，继续循环
            std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
            continue;
        }

        // 有连接到达
        if (FD_ISSET(m_iSrvSockFd, &readfds)) {
            struct sockaddr_in stClientAddr;
            socklen_t sClientLen = sizeof(stClientAddr);
            memset(&stClientAddr, 0, sClientLen);

            // 接受新连接（阻塞操作）
            int iClientSockId = accept(m_iSrvSockFd, (struct sockaddr*)&stClientAddr, &sClientLen);
            if (0 < iClientSockId) {
                // 获取客户端IP信息
                std::string strClientInfo = "";
                uint16_t ui16ClientPort = ntohs(stClientAddr.sin_port);
#ifdef _WIN32
                char* pClientAddr = inet_ntoa(stClientAddr.sin_addr);
                if (pClientAddr) {
                    strClientInfo = std::string(pClientAddr) + ":" + std::to_string(ui16ClientPort);
                }
#else
                char szClientIP[INET_ADDRSTRLEN] = {0};
                inet_ntop(AF_INET, &stClientAddr.sin_addr, szClientIP, sizeof(szClientIP));
                strClientInfo = std::string(szClientIP) + ":" + std::to_string(ui16ClientPort);
#endif

                if (strClientInfo.empty()) {
                    COMMONLOG_WARN("%s", "Parse client addr info failed.");
                    closeSocket(iClientSockId);
                } else {
                    COMMONLOG_INFO("Recv client (%s:%u) connect request.", strClientInfo.data());

                    // 保存客户端套接字并创建专用通信线程
                    m_mt4ClientFds.lock();
                    m_mp4ClientFds[iClientSockId] = strClientInfo;
                    m_mt4ClientFds.unlock();

                    // 开启数据接收线程
                    std::thread th4ClientBusiness(&TcpServer::handleClient, this, iClientSockId);
                    th4ClientBusiness.detach(); // 分离线程，自动回收资源
                }
            } else {
                if (m_bRunning) {
                    COMMONLOG_ERROR("%s", "Tcp server accept failed after select!");
                    std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
                    continue;
                } else {
                    break;
                }
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
    }
    COMMONLOG_INFO("%s", "Exit to tcp server accept connection thread.");
    return;
}

/*****************************************************
 * 功能：处理客户端业务
 * 输出参数：
 *      iClientSockFd：客户端socket通信句柄
 * ***************************************************/
void TcpServer::handleClient(int iClientSockFd)
{
    if (0 > iClientSockFd) {
        COMMONLOG_WARN("Invalid client socket fd: %d.", iClientSockFd);
        return;
    }

    COMMONLOG_INFO("Enter in recv client (%s) data thread.", getClientAddrInfo(iClientSockFd).data());

    // 设置select()参数
    struct timeval timeout{};
    timeout.tv_sec = 3; // 设置3秒超时
    timeout.tv_usec = 0;

    char szBuffer[RECVDATA_DEFAULT_SIZE] = {0};
    fd_set readfds; // 数据读取fds

    while (m_bRunning) {
        FD_ZERO(&readfds);
        FD_SET(iClientSockFd, &readfds);

        int result = select(iClientSockFd + 1, &readfds, nullptr, nullptr, &timeout);
        if (result < 0) {
            COMMONLOG_ERROR("Tcp server select client (%s) read event error!", getClientAddrInfo(iClientSockFd).data());
            break;
        } else if (0 == result) {
            // 超时，数据可读，继续循环
            std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
            continue;
        }

        // 处理读操作，读取数据
        if (FD_ISSET(iClientSockFd, &readfds)) {
            memset(szBuffer, 0, sizeof(szBuffer));
            int iRecvLen = recv(iClientSockFd, szBuffer, sizeof(szBuffer), 0);
            if (0 < iRecvLen) {
                std::string strData(szBuffer, static_cast<size_t>(iRecvLen));
                m_mt4PfnCallback.lock();
                if (m_pfn4RecvDataCb) {
                    m_pfn4RecvDataCb(strData, iClientSockFd);
                }
                m_mt4PfnCallback.unlock();
            } else if (0 == iRecvLen) {
                COMMONLOG_WARN("Client (%s) disconnected.", getClientAddrInfo(iClientSockFd).data());
                break;
            } else {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    // 非阻塞模式下，数据暂未到达，可继续等待
                    std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
                    continue;
                } else if (errno == EINTR) {
                    // 被信号中断，重试
                    std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 让出CPU时间
                    continue;
                } else if (errno == ECONNRESET) {
                    COMMONLOG_WARN("Connection reset by client (%s)!", getClientAddrInfo(iClientSockFd).data());
                    break;
                } else {
                    COMMONLOG_WARN("Receive client (%s) data failed!", getClientAddrInfo(iClientSockFd).data());
                    break;
                }
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 让出CPU时间
    }

    COMMONLOG_INFO("Exit to recv client (%s) data thread.", getClientAddrInfo(iClientSockFd).data());

    // 清理客户端资源
    closeSocket(iClientSockFd);

    // 清理缓存
    m_mt4ClientFds.lock();
    auto iter = m_mp4ClientFds.find(iClientSockFd);
    if (iter != m_mp4ClientFds.end()) {
        m_mp4ClientFds.erase(iter);
    }
    m_mt4ClientFds.unlock();
    return;
}

/*****************************************************
 * 功能：关闭socket
 * 输入参数：
 *      iSockId：需要关闭socket的id信息
 * ***************************************************/
void TcpServer::closeSocket(int iSockId)
{
    if (0 <= iSockId) {
#ifdef _WIN32
        closesocket(iSockId);
#else
        ::close(iSockId);
#endif
    }
    return;
}
